package com.admin.education.services.Implements;

import com.admin.education.common.exceptions.BadRequestException;
import com.admin.education.common.exceptions.ResourceNotFoundException;
import com.admin.education.dataTransferObject.PageRequest;
import com.admin.education.dataTransferObject.PageResponse;
import com.admin.education.dataTransferObject.ServicesExcuteResult;
import com.admin.education.dataTransferObject.templates.Mapper.TemplatesMapper;
import com.admin.education.dataTransferObject.templates.Request.CreateTemplateRequest;
import com.admin.education.dataTransferObject.templates.Request.QueryTemplateRequest;
import com.admin.education.dataTransferObject.templates.Request.UpdateTemplateRequest;
import com.admin.education.dataTransferObject.templates.TemplatesTransfer;
import com.admin.education.dataTransferObject.userAnswerSheet.Mapper.UserAnswerSheetMapper;
import com.admin.education.dataTransferObject.userAnswerSheet.Request.AnswerSheet;
import com.admin.education.dataTransferObject.userAnswerSheet.Request.CreateUserAnswersSheetRequest;
import com.admin.education.dataTransferObject.userAnswerSheet.Request.SubmitPaperRequest;
import com.admin.education.dataTransferObject.userAnswerSheet.UserAnswerSheetTransfer;
import com.admin.education.domain.event.CreateUserAnswerSheetEvent;
import com.admin.education.domain.event.CreateUserRecordEvent;
import com.admin.education.models.*;
import com.admin.education.models.Mapper.UserAnswerSheetModelMapper;
import com.admin.education.repositories.*;
import com.admin.education.services.Interfaces.ITemplateService;
import com.alibaba.fastjson.JSON;
import com.google.common.eventbus.EventBus;
import com.querydsl.jpa.impl.JPAQuery;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.joda.time.Seconds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.transaction.Transactional;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Transactional
@Validated
@Slf4j
public class TemplateService implements ITemplateService {
    private QTemplates qTemplates;
    private QUserInformation qUserInformation;
    private QIntegrals qIntegrals;
    private QDesignations qDesignations;
    private QUserAnswerSheet qUserAnswerSheet;
    private QWaitForChallenges qWaitForChallenges;
    @Autowired
    private TemplatesMapper templatesMapper;
    @Autowired
    private UserAnswerSheetMapper userAnswerSheetMapper;
    @Autowired
    private UserAnswerSheetModelMapper userAnswerSheetModelMapper;
    @Autowired
    private TemplateRepository templateRepository;
    @Autowired
    private ApplyCrowdRepository applyCrowdRepository;
    @Autowired
    private TemplateQuestionsRepository templateQuestionsRepository;
    @Autowired
    private TemplateAnswersRepository templateAnswersRepository;
    @Autowired
    private UserInformationRepository userInformationRepository;
    @Autowired
    private UserAnswerSheetRepository userAnswerSheetRepository;
    @Autowired
    private UserAnswerSheetOwnRepository userAnswerSheetOwnRepository;
    @Autowired
    private UserAnswerSheetQuestionRepository userAnswerSheetQuestionRepository;
    @Autowired
    private UserAnswerSheetAnswerRepository userAnswerSheetAnswerRepository;
    @Autowired
    private IntegralsRepository integralsRepository;
    @Autowired
    private EventBus eventBus;
    @Autowired
    private DesignationsRepository designationsRepository;
    @Autowired
    private WaitForChallengesRepository waitForChallengesRepository;

    public TemplateService() {
        qTemplates = QTemplates.templates;
        qUserInformation = QUserInformation.userInformation;
        qIntegrals = QIntegrals.integrals;
        qUserAnswerSheet = QUserAnswerSheet.userAnswerSheet;
        qDesignations = QDesignations.designations;
        qWaitForChallenges = QWaitForChallenges.waitForChallenges;
    }

    @Override
    public ServicesExcuteResult<PageResponse<TemplatesTransfer>> queryTemplates(PageRequest pageRequest) {
        ServicesExcuteResult<PageResponse<TemplatesTransfer>> servicesExcuteResult = new ServicesExcuteResult<>();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.valid.eq(true));

        long totalCount = templatesJPAQuery.fetchCount();

        templatesJPAQuery = templatesJPAQuery.orderBy(qTemplates.name.asc()).offset((pageRequest.getPageIndex() - 1) * pageRequest.getPageSize()).limit(pageRequest.getPageSize());

        List<Templates> templates = templatesJPAQuery.fetch();

        List<TemplatesTransfer> templatesTransfers = templatesMapper.mapFrom(templates);

        PageResponse<TemplatesTransfer> templatesTransferPageResponse = new PageResponse<>(pageRequest.getPageSize(), pageRequest.getPageIndex(), (int) totalCount, templatesTransfers);

        servicesExcuteResult.setData(templatesTransferPageResponse);
        servicesExcuteResult.setSuccess(true);
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult<TemplatesTransfer> queryTemplateById(long id) throws ResourceNotFoundException {
        ServicesExcuteResult<TemplatesTransfer> servicesExcuteResult = new ServicesExcuteResult<>();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.id.eq(id), qTemplates.valid.eq(true));

        if (0 != templatesJPAQuery.fetchCount()) {
            TemplatesTransfer templatesTransfer = new TemplatesTransfer();
            templatesTransfer.setRoot(true);

            templatesTransfer = templatesMapper.mapFrom(templatesJPAQuery.fetchOne(), templatesTransfer);

            servicesExcuteResult.setData(templatesTransfer);
            servicesExcuteResult.setSuccess(true);

        } else {
            throw new ResourceNotFoundException("模板不存在");
        }
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult<UserAnswerSheetTransfer> createUserAnswerSheet(long id, long userInformationId, long roleId, @Valid SubmitPaperRequest submitPaperRequest) throws ResourceNotFoundException {
        ServicesExcuteResult servicesExcuteResult = new ServicesExcuteResult();
        JPAQuery<UserInformation> userInformationJPAQuery = this.userInformationRepository.selectFrom(qUserInformation).where(qUserInformation.id.eq(userInformationId), qUserInformation.valid.eq(true));

        if (0 == userInformationJPAQuery.fetchCount()) {
            servicesExcuteResult.setErrorMessage("用户信息不存在或没效");
            return servicesExcuteResult;
        }
        JPAQuery<Integrals> integralsJPAQuery = this.integralsRepository.selectFrom(qIntegrals).where(qIntegrals.userInformation.id.eq(userInformationId));
        Integrals integrals = null;
        if (0 == integralsJPAQuery.fetchCount()) {
            integrals = new Integrals();
            integrals.setUserInformation(userInformationJPAQuery.fetchOne());
            integrals.setValue(new BigDecimal(0));

            this.integralsRepository.persist(integrals);
        } else {
            integrals = integralsJPAQuery.fetchFirst();
        }

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.id.eq(id), qTemplates.valid.eq(true));
        if (0 != templatesJPAQuery.fetchCount()) {
            Templates templates = templatesJPAQuery.fetchOne();

            if (templates.getApplyCrowds().stream().anyMatch(p -> p.getRole().getId() == roleId)) {

                if (!templates.isAllowMultipleAnswer()) {
                    if (templates.getUserAnswerSheets().stream().anyMatch(p -> p.getUserInformation().getId() == userInformationId)) {
                        servicesExcuteResult.setErrorMessage("该答卷只运行回答一次");
                        return servicesExcuteResult;
                    }
                }

                CreateUserAnswerSheetEvent createUserAnswerSheetEvent = new CreateUserAnswerSheetEvent();

                createUserAnswerSheetEvent.setTemplates(templates);
                createUserAnswerSheetEvent.setSubmitPaperRequest(submitPaperRequest);
                createUserAnswerSheetEvent.setUserInformation(userInformationJPAQuery.fetchOne());

                UserAnswerSheet userAnswerSheets = this.createUserAnswerSheet(createUserAnswerSheetEvent);
                DateTime dt1 = new DateTime(submitPaperRequest.getBeginTime());
                DateTime dt2 = new DateTime(submitPaperRequest.getEndTime());
                int seconds = Seconds.secondsBetween(dt1, dt2).getSeconds() % 60;

                int questionSize = templates.getTemplateQuestions().size();

                BigDecimal mark = BigDecimal.ZERO;

                if (0 == questionSize)
                    userAnswerSheets.setTotalMark(BigDecimal.valueOf(0));
                else
                    userAnswerSheets.setTotalMark(userAnswerSheets.getTotalMark().add(userAnswerSheets.getTotalMark().multiply(BigDecimal.valueOf(questionSize * 60 - seconds).divide(BigDecimal.valueOf(questionSize * 60), new MathContext(2, RoundingMode.HALF_UP)))));

                if (BigDecimal.ZERO.compareTo(userAnswerSheets.getTemplateMark()) > 0) {
                    userAnswerSheets.setTotalMark(BigDecimal.ZERO);
                }

                UserAnswerSheetTransfer userAnswerSheetTransfer = userAnswerSheetMapper.mapFrom(userAnswerSheets);
                integrals.setValue(integrals.getValue().add(userAnswerSheets.getTotalMark()));

                userAnswerSheetTransfer.setCurrentTotalMark(integrals.getValue());
                userAnswerSheetTransfer.setQuestionsSize(questionSize);

                this.userAnswerSheetRepository.merge(userAnswerSheets);

                long greaterCount = this.userAnswerSheetRepository.selectFrom(qUserAnswerSheet).where(qUserAnswerSheet.totalMark.loe(userAnswerSheets.getTotalMark())).fetchCount();
                long totalCount = this.userAnswerSheetRepository.selectFrom(qUserAnswerSheet).fetchCount();

                Designations designations = null;
                if (0 == totalCount) {
                    designations = this.designationsRepository.selectFrom(qDesignations).where(qDesignations.valid.eq(true)).fetchFirst();
                } else if (0L == greaterCount) {
                    designations = this.designationsRepository.selectFrom(qDesignations).where(qDesignations.valid.eq(true)).orderBy(qDesignations.proportion.desc()).fetchFirst();
                } else {
                    BigDecimal ratio = BigDecimal.valueOf(greaterCount).divide(BigDecimal.valueOf(totalCount), new MathContext(4, RoundingMode.HALF_UP));
                    designations = this.designationsRepository.selectFrom(qDesignations).where(qDesignations.valid.eq(true)).where(qDesignations.proportion.loe(ratio.multiply(BigDecimal.valueOf(100)))).fetchFirst();

                }

                if (null != designations) {
                    integrals.setDesignation(designations.getName());

                    userAnswerSheetTransfer.setDesignationName(designations.getName());
                    userAnswerSheets.setDesignationName(designations.getName());
                }

                this.waitForChallengesRepository.update(qWaitForChallenges).set(qWaitForChallenges.completed, true).where(qWaitForChallenges.userInformationId.eq(userInformationId), qWaitForChallenges.templates.id.eq(templates.getId())).execute();
                this.integralsRepository.merge(integrals);
                servicesExcuteResult.setData(userAnswerSheetTransfer);
                servicesExcuteResult.setSuccess(true);


                CreateUserRecordEvent createUserRecordEvent = new CreateUserRecordEvent();
                createUserRecordEvent.setUserInformationId(userInformationId);
                createUserRecordEvent.setRecord("完成了"+templates.getName()+"答卷");

                this.eventBus.post(createUserRecordEvent);
            } else {
                servicesExcuteResult.setErrorMessage("该题库不适用你这个角色");
            }
        } else {
            throw new ResourceNotFoundException("模板不存在");
        }
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult<List<UserAnswerSheetTransfer>> queryUserAnswerSheetByTemplateId(long id, long userInformationId) throws ResourceNotFoundException {
        ServicesExcuteResult<List<UserAnswerSheetTransfer>> servicesExcuteResult = new ServicesExcuteResult();
        JPAQuery<UserInformation> userInformationJPAQuery = this.userInformationRepository.selectFrom(qUserInformation).where(qUserInformation.id.eq(userInformationId));

        if (0 == userInformationJPAQuery.fetchCount()) {
            UserInformation userInformation = userInformationJPAQuery.fetchOne();
            List<UserAnswerSheet> userAnswerSheets = userInformation.getUserAnswerSheets().stream().filter(p -> p.getTemplates().getId() == id).collect(Collectors.toList());

            servicesExcuteResult.setData(userAnswerSheetMapper.mapFrom(userAnswerSheets));
            servicesExcuteResult.setSuccess(true);
        } else {
            throw new ResourceNotFoundException("用户不存在");
        }
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult createTemplate(@Valid CreateTemplateRequest createTemplateRequest) throws BadRequestException {
        ServicesExcuteResult servicesExcuteResult = new ServicesExcuteResult();

        Templates templates = this.templatesMapper.mapFrom(createTemplateRequest);

        this.templateRepository.persist(templates);
        if (null != templates.getTemplateQuestions())
            this.templateQuestionsRepository.persist(templates.getTemplateQuestions());

        for (TemplateQuestions templateQuestions : templates.getTemplateQuestions()) {
            if (null != templateQuestions.getTemplateAnswers())
                this.templateAnswersRepository.persist(templateQuestions.getTemplateAnswers());
        }
        if (null != templates.getApplyCrowds()) {
            this.applyCrowdRepository.persist(templates.getApplyCrowds());

            for (ApplyCrowd applyCrowd : templates.getApplyCrowds()) {
                log.info(String.valueOf(applyCrowd.getRole().getUserInformations().size()));
                for (UserInformation userInformation : applyCrowd.getRole().getUserInformations()) {

                    WaitForChallenges waitForChallenges = new WaitForChallenges();
                    waitForChallenges.setUserInformationId(userInformation.getId());
                    waitForChallenges.setTemplates(templates);
                    waitForChallenges.setCompleted(false);

                    log.info(JSON.toJSONString(waitForChallenges));
                    this.waitForChallengesRepository.persist(waitForChallenges);
                }
            }
        }

        servicesExcuteResult.setSuccess(true);
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult updateTemplate(long templateId, @Valid UpdateTemplateRequest updateTemplateRequest) throws ResourceNotFoundException, BadRequestException {
        ServicesExcuteResult servicesExcuteResult = new ServicesExcuteResult();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.id.eq(templateId));
        if (0 != templatesJPAQuery.fetchCount()) {
            Templates templates = templatesJPAQuery.fetchOne();

            templates = this.templatesMapper.mapFrom(updateTemplateRequest, templates);

            for (ApplyCrowd applyCrowd : templates.getApplyCrowds()) {
                this.applyCrowdRepository.remove(applyCrowd);
            }
            templates.getCategorys().clear();

            for (TemplateQuestions templateQuestions : templates.getTemplateQuestions()) {
                for (TemplateAnswers templateAnswers : templateQuestions.getTemplateAnswers()) {
                    this.templateAnswersRepository.remove(templateAnswers);
                }
                this.templateQuestionsRepository.remove(templateQuestions);
            }

            this.templateRepository.merge(templates);
            if (null != templates.getTemplateQuestions())
                this.templateQuestionsRepository.persist(templates.getTemplateQuestions());

            for (TemplateQuestions templateQuestions : templates.getTemplateQuestions()) {
                if (null != templateQuestions.getTemplateAnswers())
                    this.templateAnswersRepository.persist(templateQuestions.getTemplateAnswers());
            }
            if (null != templates.getApplyCrowds()) {
                this.applyCrowdRepository.persist(templates.getApplyCrowds());
                for (ApplyCrowd applyCrowd : templates.getApplyCrowds()) {
                    for (UserInformation userInformation : applyCrowd.getRole().getUserInformations()) {
                        if (0 == this.waitForChallengesRepository.selectFrom(qWaitForChallenges).where(qWaitForChallenges.userInformationId.eq(userInformation.getId()), qWaitForChallenges.templates.id.eq(templateId)).fetchCount()) {
                            WaitForChallenges waitForChallenges = new WaitForChallenges();
                            waitForChallenges.setUserInformationId(userInformation.getId());
                            waitForChallenges.setTemplates(templates);
                            waitForChallenges.setCompleted(false);

                            this.waitForChallengesRepository.persist(waitForChallenges);
                        }
                    }
                }
            }

            servicesExcuteResult.setSuccess(true);
        } else {
            throw new ResourceNotFoundException("模板不存在");
        }
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult deleteTemplate(long templateId) throws ResourceNotFoundException {
        ServicesExcuteResult servicesExcuteResult = new ServicesExcuteResult();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.id.eq(templateId));
        if (0 != templatesJPAQuery.fetchCount()) {
            Templates templates = templatesJPAQuery.fetchOne();

            this.waitForChallengesRepository.delete(qWaitForChallenges).where(qWaitForChallenges.templates.id.eq(templateId)).execute();

            this.templateRepository.remove(templates);
            servicesExcuteResult.setSuccess(true);
        } else {
            throw new ResourceNotFoundException("模板不存在");
        }
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult updateTemplateValid(long templateId) throws ResourceNotFoundException {
        ServicesExcuteResult servicesExcuteResult = new ServicesExcuteResult();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.id.eq(templateId));
        if (0 != templatesJPAQuery.fetchCount()) {
            Templates templates = templatesJPAQuery.fetchOne();

            templates.setValid(!templates.isValid());

            this.templateRepository.merge(templates);

            servicesExcuteResult.setSuccess(true);
        } else {
            throw new ResourceNotFoundException("模板不存在");
        }
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult<PageResponse<TemplatesTransfer>> queryTemplates(@Valid QueryTemplateRequest queryTemplateRequest) {
        ServicesExcuteResult<PageResponse<TemplatesTransfer>> servicesExcuteResult = new ServicesExcuteResult<>();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).where(qTemplates.valid.eq(true));

        if (!StringUtils.isEmpty(queryTemplateRequest.getName())) {
            templatesJPAQuery = templatesJPAQuery.where(qTemplates.name.startsWith(queryTemplateRequest.getName()));
        }
        long totalCount = templatesJPAQuery.fetchCount();

        templatesJPAQuery = templatesJPAQuery.orderBy(qTemplates.name.asc()).offset((queryTemplateRequest.getPageIndex() - 1) * queryTemplateRequest.getPageSize()).limit(queryTemplateRequest.getPageSize());

        List<Templates> templates = templatesJPAQuery.fetch();

        List<TemplatesTransfer> templatesTransfers = templatesMapper.mapFrom(templates);

        PageResponse<TemplatesTransfer> templatesTransferPageResponse = new PageResponse<>(queryTemplateRequest.getPageSize(), queryTemplateRequest.getPageIndex(), (int) totalCount, templatesTransfers);

        servicesExcuteResult.setData(templatesTransferPageResponse);
        servicesExcuteResult.setSuccess(true);
        return servicesExcuteResult;
    }

    @Override
    public ServicesExcuteResult<PageResponse<TemplatesTransfer>> queryWaitForChallenge(@Valid PageRequest pageRequest, long userInformationId, boolean challenge) {
        ServicesExcuteResult<PageResponse<TemplatesTransfer>> servicesExcuteResult = new ServicesExcuteResult<>();

        JPAQuery<Templates> templatesJPAQuery = this.templateRepository.selectFrom(qTemplates).innerJoin(qTemplates.waitForChallenges, qWaitForChallenges).where(qWaitForChallenges.userInformationId.eq(userInformationId), qWaitForChallenges.completed.eq(challenge)).where(qTemplates.valid.eq(true)).distinct();

        long totalCount = templatesJPAQuery.fetchCount();

        templatesJPAQuery = templatesJPAQuery.orderBy(qTemplates.name.asc()).offset((pageRequest.getPageIndex() - 1) * pageRequest.getPageSize()).limit(pageRequest.getPageSize());

        List<Templates> templates = templatesJPAQuery.fetch();

        List<TemplatesTransfer> templatesTransfers = templatesMapper.mapFrom(templates);

        PageResponse<TemplatesTransfer> templatesTransferPageResponse = new PageResponse<>(pageRequest.getPageSize(), pageRequest.getPageIndex(), (int) totalCount, templatesTransfers);

        servicesExcuteResult.setData(templatesTransferPageResponse);
        servicesExcuteResult.setSuccess(true);
        return servicesExcuteResult;
    }

    private UserAnswerSheet createUserAnswerSheet(CreateUserAnswerSheetEvent createUserAnswerSheetEvent) {
        Templates templates = createUserAnswerSheetEvent.getTemplates();
        List<CreateUserAnswersSheetRequest> createUserAnswersSheetRequests = createUserAnswerSheetEvent.getSubmitPaperRequest().getAnswers();

        UserAnswerSheet userAnswerSheet = new UserAnswerSheet();
        userAnswerSheet.setBeginTime(createUserAnswerSheetEvent.getSubmitPaperRequest().getBeginTime());
        userAnswerSheet.setEndTime(createUserAnswerSheetEvent.getSubmitPaperRequest().getEndTime());

        userAnswerSheet.setName(templates.getName());
        userAnswerSheet.setTemplates(templates);
        userAnswerSheet.setUserInformation(createUserAnswerSheetEvent.getUserInformation());
        userAnswerSheet.setTemplateMark(templates.getTemplateMark());
        userAnswerSheet.setCreateTime(new Date());
        userAnswerSheet.setUserAnswerSheetQuestions(new ArrayList<>());

        this.userAnswerSheetRepository.persist(userAnswerSheet);

        for (TemplateQuestions questions : templates.getTemplateQuestions()) {
            final long tempalteQuestionId = questions.getId();

            UserAnswerSheetQuestion userAnswerSheetQuestion = userAnswerSheetModelMapper.mapFrom(questions);

            if (0 == createUserAnswersSheetRequests.stream().filter(p -> tempalteQuestionId == p.getQuestionId()).count()) {
                continue;
            } else {
                CreateUserAnswersSheetRequest createUserAnswersSheetRequest = createUserAnswersSheetRequests.stream().filter(p -> tempalteQuestionId == p.getQuestionId()).findAny().get();
                for (AnswerSheet answerSheet : createUserAnswersSheetRequest.getAnswers()) {

                    UserAnswerSheetOwn userAnswerSheetOwn = new UserAnswerSheetOwn();

                    userAnswerSheetOwn.setAnswer(answerSheet.getAnswerDescription());
                    userAnswerSheetOwn.setUserAnswerSheetQuestion(userAnswerSheetQuestion);

                    userAnswerSheetQuestion.getUserAnswerSheetOwns().add(userAnswerSheetOwn);
                }
            }

            userAnswerSheetQuestion.setUserAnswerSheet(userAnswerSheet);
            userAnswerSheetQuestion.calculation();
            this.userAnswerSheetQuestionRepository.persist(userAnswerSheetQuestion);

            this.userAnswerSheetOwnRepository.persist(userAnswerSheetQuestion.getUserAnswerSheetOwns());
            this.userAnswerSheetAnswerRepository.persist(userAnswerSheetQuestion.getUserAnswerSheetAnswers());

            userAnswerSheet.getUserAnswerSheetQuestions().add(userAnswerSheetQuestion);
        }
        return userAnswerSheet;
    }
}
